﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using DrawableObjects.Actors.Aircrafts;
using DrawableObjects.Actors;
using DrawableObjects.Actors.Weapons;
using GlobalGameClasses;
using Sirian.Serialization;
using System.Text.RegularExpressions;

namespace Serialization.Script
{
    /// <summary>
    /// Parses the LevelScript textfile and generates a LevelScript class.
    /// </summary>
    public class ScriptReader
    {
        private string filePath;
        private const string LINEREGEX = "^\\s*\\(\\s*\\d+\\s*\\)\\s*\\w+\\s*:(\\s*\\w+\\s*=\\s*((\\[\\s*(((((\\s*-?\\d+\\s*,)*\\s*-?\\d+\\|)*(\\s*-?\\d+\\s*,)*\\s*-?\\d+))|((((\\s*\\w+\\s*,)*\\s*\\w+\\s*\\|)*(\\s*\\w+\\s*,)*\\s*\\w+)))\\s*\\])|(\\w+))\\s*;)*\\s*$";

        public ScriptReader(string filePath)
        {
            this.filePath = filePath;
        }

        /// <summary>
        /// Parses the level script text file.
        /// </summary>
        /// <returns>A LevelScript object that represents the text file's information.</returns>
        public LevelScript readLevelScript(LogWriter logWriter)
        {
            List<ScriptedEvent> events = new List<ScriptedEvent>();
            String input;
            using (StreamReader sr = new StreamReader(filePath))
            {
                input = sr.ReadToEnd();
            }
            input = input.Replace("\r\n", "");
            String[] commands = input.Split(new string[1] { "--" }, StringSplitOptions.RemoveEmptyEntries);
            int time;
            Regex lineValidator = new Regex(LINEREGEX);
            String[] split_command_params;
            String[] split_time_command;
            String[] parameters;
            for (int i = 0; i < commands.Length; i++)
            {
                if (lineValidator.IsMatch(commands[i]))
                {
                    List<string> paramNames = new List<string>();
                    List<string> paramValues = new List<string>();

                    split_command_params = commands[i].Split(new string[1] { ":" }, StringSplitOptions.RemoveEmptyEntries);
                    split_time_command = split_command_params[0].Split(new string[1] { ")" }, StringSplitOptions.RemoveEmptyEntries);
                    bool parseSuccess = Int32.TryParse(split_time_command[0].Trim().Substring(1).Trim(), out time);
                    if (parseSuccess)
                    {
                        parameters = split_command_params[1].Split(new string[1] { ";" }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string param in parameters)
                        {
                            string[] split_param = param.Split(new string[1] { "=" }, StringSplitOptions.RemoveEmptyEntries);
                            paramNames.Add(split_param[0].Trim().ToUpper());
                            paramValues.Add(split_param[1].Trim().ToLower());
                        }
                        if (split_time_command[1].Trim() == "SETMUSIC")
                        {
                            try
                            {
                                events.Add(new Event_SetMusic(getParamValue(paramNames, paramValues, "MUSICNAME"), time));
                            }
                            catch (KeyNotFoundException ex)
                            {
                                logWriter.writeError("Script: Line " + i + ":" + ex.Message);
                            }
                        }
                        else if (split_time_command[1] == "SETBOSS")
                        {
                            try
                            {
                                string aircraft = getParamValue(paramNames, paramValues, "BOSSNAME");
                                Aircraft aircraftBoss = getAircaftFromKey(aircraft);
                                aircraftBoss.owner = Owner.ENEMY;
                                events.Add(new Event_CreateBoss(aircraftBoss, time));
                            }
                            catch (KeyNotFoundException ex)
                            {
                                logWriter.writeError("Script: Line " + i + ":" + ex.Message);
                            }
                        }
                        else if (split_time_command[1] == "SETWAVE")
                        {

                            try
                            {
                                events.Add(createWave(paramNames, paramValues, time));
                            }
                            catch (Exception ex)
                            {
                                logWriter.writeError("Script: Line " + i + ":" + ex.Message);
                            }
                        }
                    }
                    else
                    {
                        logWriter.writeError("Script: Line " + i + ":" + split_time_command[0].Trim().Substring(1).Trim() + "is not a valid value for time.");
                    }
                }
                else
                {
                    logWriter.writeError("Script: Line " + i + ": Syntax error.");
                }
            }
            return new LevelScript(events);
        }

        /// <summary>
        /// Creates an aircraft wave from parsed parameters.
        /// </summary>
        /// <param name="paramNames">The parameters names.</param>
        /// <param name="paramValues">The parameters values.</param>
        /// <param name="time">The time for the creation to occur.</param>
        /// <returns>The scripted event creating the wave.</returns>
        private ScriptedEvent createWave(List<string> paramNames, List<string> paramValues, int time)
        {
            Formation formation = getFormationFromKey(getParamValue(paramNames, paramValues, "FORMATION"));

            //Read path data
            string pointstring = getParamValue(paramNames, paramValues, "PATH");
            pointstring = pointstring.Substring(1, pointstring.Length - 2);
            string[] points = pointstring.Split('|');
            List<Vector2> pointList = new List<Vector2>();
            foreach (string myPoint in points)
            {
                string[] coordinates = myPoint.Trim().Split(',');
                float tempX;
                float tempY;
                if (float.TryParse(coordinates[0].Trim(),out tempX) && float.TryParse(coordinates[1].Trim(),out tempY)) 
                {
                    pointList.Add(new Vector2(tempX, tempY));
                } 
                else 
                {
                    throw new FormatException("'" + coordinates[0].Trim() + "," + coordinates[1].Trim() + "' is not a valid Point!");
                }
            }
            Serialization.Script.Path myPath = new Serialization.Script.Path(pointList);

            //read aircraft data
            string aircraftstring = getParamValue(paramNames, paramValues, "AIRCRAFTS");
            aircraftstring = aircraftstring.Substring(1, aircraftstring.Length - 2);
            string[] aircrafts = aircraftstring.Split('|');
            List<Aircraft> aircraftList = new List<Aircraft>();
            foreach (string myAircraft in aircrafts)
            {
                string[] aircraft_split = myAircraft.Trim().Split(',');
                Aircraft currentAircraft = getAircaftFromKey(aircraft_split[0].Trim());
                if (aircraft_split.Length > 1)
                {
                    Item currentItem = getItemFromKey(aircraft_split[1].Trim());
                    currentAircraft.dropItem = currentItem;
                }
                currentAircraft.owner = Owner.ENEMY;
                aircraftList.Add(currentAircraft);
            }

            return new Event_CreateEnemyWave(aircraftList, formation, myPath, time);
        }

        private string getParamValue(List<string> parameters, List<string> values, string key)
        {
            int i = parameters.IndexOf(key);
            if (i < 0) throw new ArgumentException("Parameter '" + key + "' is undefined");
            return values[i];
        }

        private Item getItemFromKey(string identifier)
        {
            Item item = null;
            string key = identifier.ToUpper();
            if (key == "UPGRADE_PLASMA")
            {
                item = WeaponUpgrade.GET_UPGRADEITEM(Weapon.WeaponType.Plasma);
            }
            else if (key == "UPGRADE_MISSILE")
            {
                item = WeaponUpgrade.GET_UPGRADEITEM(Weapon.WeaponType.Missile);
            }
            else if (key == "UPGRADE_LASER")
            {
                item = WeaponUpgrade.GET_UPGRADEITEM(Weapon.WeaponType.Laser);
            }
            else 
            {
                throw new KeyNotFoundException("'key" + key + "' is not a valid Itemidentifier");
            }
            return item;
        }

        private Aircraft getAircaftFromKey(string identifier)
        {
            Aircraft aircraft = null;
            string key = identifier.ToUpper();
            Matrix standardMatrix = Matrix.CreateWorld(new Vector3(0, GlobalGameValues.GAMEFIELD_HEIGHT, 0), Vector3.Backward, Vector3.Up);
            if (key == "PLAYER")
            {
                aircraft = SpecialEffectsAircraft.GET_PLAYER_SHIP(new Matrix());
            }
            else if (key == "DRONE")
            {
                aircraft = SpecialEffectsAircraft.GET_DRONE(standardMatrix);
            }
            else if (key == "ENEMY_1")
            {
                aircraft = FabricatableAircraft.GET_ENEMY_1(standardMatrix);
            }
            else if (key == "ENEMY_2")
            {
                aircraft = FabricatableAircraft.GET_ENEMY_2(standardMatrix);
            }
            else if (key == "ENEMY_3")
            {
                aircraft = FabricatableAircraft.GET_ENEMY_3(standardMatrix);
            }
            else if (key == "ENEMY_4")
            {
                aircraft = FabricatableAircraft.GET_ENEMY_4(standardMatrix);
            }
            else if (key == "ENEMY_5")
            {
                aircraft = FabricatableAircraft.GET_ENEMY_5(standardMatrix);
            }
            else if (key == "BOSS")
            {
                aircraft = SpecialEffectsAircraft.GET_BOSS(standardMatrix);
            }
            else
            {
                throw new KeyNotFoundException("'" + key + "' is not a valid Aircraftidentfier");
            }
            return aircraft;
        }

        private Formation getFormationFromKey(string identifier)
        {
            Formation formation = null;
            string key = identifier.ToUpper();
            int i = -1;
            while (formation == null && ++i < Formation.formations.Length)
            {
                if (Formation.formations[i].name == key)
                {
                    formation = Formation.formations[i];
                }
            }
            if (formation == null) throw new KeyNotFoundException("'" + key + "' is not a valid Formationidentfier");
            return formation;
        }
    }
}
